#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
五子棋AI适配器 - 将MCTS+神经网络AI与现有游戏系统集成
"""

import time
import random
from typing import Tuple, List, Dict, Any

# 从MCTS实现导入所需组件
from gobang_ai_mcts import AIPlayer as MCTSAIPlayer, Constants as MCTSConstants, BoardManager

# 适配现有游戏系统的常量映射
class ConstantsMapper:
    """
    常量映射类，用于在两个系统之间转换常量定义
    """
    # 玩家标识映射
    PLAYER_MAP = {
        1: 1,  # 黑棋保持一致
        2: 2   # 白棋保持一致
    }
    
    # 棋盘大小（确保两个系统一致）
    BOARD_SIZE = 15
    
    @staticmethod
    def map_player(game_player: int) -> int:
        """将游戏的玩家标识映射到MCTS系统的玩家标识"""
        if game_player == 1:  # 黑棋
            return MCTSConstants.BLACK
        elif game_player == 2:  # 白棋
            return MCTSConstants.WHITE
        return MCTSConstants.EMPTY
    
    @staticmethod
    def map_move(mcts_move: Tuple[int, int]) -> Tuple[int, int]:
        """将MCTS系统的落子位置映射回游戏系统"""
        # 两个系统使用相同的坐标系统，直接返回
        return mcts_move
    
    @staticmethod
    def map_board(board: List[List[int]]) -> List[List[int]]:
        """将游戏系统的棋盘格式映射到MCTS系统"""
        # 直接返回棋盘，因为两个系统使用相同的表示方式
        return board
    
    @staticmethod
    def map_back_move(mcts_move: Tuple[int, int]) -> Tuple[int, int]:
        """将MCTS系统的落子位置映射回游戏系统"""
        # 直接返回，因为两个系统使用相同的坐标系统
        return mcts_move

class AIMCTSPlayer:
    """
    MCTS+神经网络AI适配器类，用于与现有的五子棋游戏系统集成
    实现与原AIPlayer相同的接口，使游戏系统无需修改即可使用新AI
    """
    
    # 难度配置映射
    DIFFICULTY_MAP = {
        "easy": 1,
        "normal": 2, 
        "medium": 3,
        "hard": 4,
        "expert": 5
    }
    
    def __init__(self, difficulty: str = "medium"):
        """
        初始化AI适配器
        
        Args:
            difficulty: AI难度级别
        """
        # 转换难度设置为AIDifficulty枚举
        from gobang_ai_mcts import AIDifficulty
        
        # 获取难度数值
        difficulty_num = self.DIFFICULTY_MAP.get(difficulty, 3)
        
        # 创建对应的AIDifficulty枚举实例
        try:
            mcts_difficulty = AIDifficulty(difficulty_num)
        except (ValueError, TypeError):
            # 如果出错，默认为中等难度
            mcts_difficulty = AIDifficulty(2)  # 使用枚举值，而不是数字
        
        # 创建MCTS AI实例
        self.mcts_ai = MCTSAIPlayer(difficulty=mcts_difficulty)
        self.player = 2  # 默认AI执白
        self.human_player = 1
        self.search_depth = 3  # 兼容原接口，但MCTS不使用固定深度
    
    def set_search_depth(self, depth: int):
        """
        兼容原接口的设置搜索深度方法
        MCTS不使用固定深度，但可以调整搜索参数
        
        Args:
            depth: 搜索深度（会映射到模拟次数）
        """
        # 将深度映射到MCTS参数
        if depth <= 1:
            self.mcts_ai.config['simulations'] = 100  # 简单
        elif depth <= 2:
            self.mcts_ai.config['simulations'] = 200  # 普通
        elif depth <= 3:
            self.mcts_ai.config['simulations'] = 300  # 中等
        elif depth <= 4:
            self.mcts_ai.config['simulations'] = 400  # 困难
        else:
            self.mcts_ai.config['simulations'] = 500  # 专家
    
    def find_best_move(self, board: List[List[int]], player: int = None, 
                      max_time: float = 1.0, use_alpha_beta: bool = True,
                      prune_factor: float = 0.8, max_candidates: int = 15) -> Tuple[int, int]:
        """
        查找最佳落子位置，实现与原AIPlayer相同的接口
        增强版：优化模拟次数、难度分级和威胁检测逻辑
        
        Args:
            board: 当前棋盘状态
            player: 当前玩家（1-黑，2-白）
            max_time: 最大思考时间（秒）
            use_alpha_beta: 是否使用Alpha-Beta剪枝（MCTS忽略）
            prune_factor: 剪枝因子（MCTS忽略）
            max_candidates: 最大候选点数量（MCTS忽略）
            
        Returns:
            最佳落子位置 (row, col)
        """
        # 如果没有指定玩家，使用初始化时设置的玩家
        if player is None:
            player = self.player
        
        # 转换玩家标识
        mcts_player = ConstantsMapper.map_player(player)
        
        # 计算已落子数量和总棋盘大小，用于评估游戏进度
        filled_cells = sum(row.count(1) + row.count(2) for row in board)
        board_size = len(board) if board else ConstantsMapper.BOARD_SIZE
        total_cells = board_size * board_size
        game_progress = filled_cells / total_cells  # 范围从0到1
        
        # 备份原始配置以便恢复
        original_simulations = self.mcts_ai.config.get('num_simulations', 300)
        original_thinking_time = self.mcts_ai.config.get('thinking_time', 1.0)
        
        # 根据难度级别设置不同的模拟次数，提高AI智能水平
        difficulty_level = self.mcts_ai.difficulty.value
        
        # 针对不同难度级别设置合理的基础模拟次数
        if difficulty_level <= 1:  # 简单难度
            base_simulations = 100
            thinking_time_limit = 0.4  # 秒
        elif difficulty_level <= 2:  # 普通难度
            base_simulations = 200
            thinking_time_limit = 0.6  # 秒
        elif difficulty_level <= 3:  # 中等难度
            base_simulations = 300
            thinking_time_limit = 0.8  # 秒
        elif difficulty_level <= 4:  # 困难难度
            base_simulations = 400
            thinking_time_limit = 1.0  # 秒
        else:  # 专家难度
            base_simulations = 500
            thinking_time_limit = 1.2  # 秒
        
        # 根据游戏进度动态调整模拟次数
        # 游戏初期：使用基础模拟次数
        # 游戏中期：略微增加模拟次数
        # 游戏后期：显著增加模拟次数以应对复杂局面
        if game_progress < 0.3:  # 初期
            adjusted_simulations = base_simulations
        elif game_progress < 0.7:  # 中期
            adjusted_simulations = int(base_simulations * 1.2)
        else:  # 后期
            adjusted_simulations = int(base_simulations * 1.5)
        
        # 应用设置
        self.mcts_ai.config['num_simulations'] = adjusted_simulations
        self.mcts_ai.config['thinking_time'] = min(max_time, thinking_time_limit)
        
        # 根据难度决定线程数 - 更高难度可以使用更多线程
        if difficulty_level <= 2:
            self.mcts_ai.config['num_threads'] = 1  # 简单/普通难度使用单线程
        else:
            self.mcts_ai.config['num_threads'] = 2  # 中等及以上难度使用双线程
        
        # 快速检查：如果棋盘为空或只有少数棋子，使用改进的启发式落子
        empty_count = sum(row.count(0) for row in board)
        if empty_count >= board_size * board_size - 5:  # 前5步使用简单启发式
            # 开局阶段，使用改进的启发式方法
            center = board_size // 2
            
            # 检查是否为空棋盘
            if empty_count == board_size * board_size:
                return (center, center)
                
            # 检查中心点附近是否有棋子
            has_near_center = False
            for i in range(max(0, center - 1), min(board_size, center + 2)):
                for j in range(max(0, center - 1), min(board_size, center + 2)):
                    if board[i][j] != 0:
                        has_near_center = True
                        break
                if has_near_center:
                    break
                    
            # 如果中心附近没有棋子，直接返回中心
            if not has_near_center:
                return (center, center)
        
        # 记录开始时间用于调试
        start_time = time.time()
        
        # 第一阶段：检查关键威胁（优先级最高）
        critical_move = None
        if hasattr(self.mcts_ai, '_check_critical_moves'):
            critical_move = self.mcts_ai._check_critical_moves(board, mcts_player)
            
            if critical_move:
                elapsed = (time.time() - start_time) * 1000
                print(f"[AI调试] 检测到关键威胁，立即响应: 位置={critical_move}, 耗时={elapsed:.2f}ms")
                # 对于低难度，可能不总是选择最优防守
                if difficulty_level <= 1 and random.random() < 0.2:  # 简单难度20%概率不选择最佳防守
                    pass  # 继续进行MCTS搜索
                else:
                    # 恢复原始配置
                    self.mcts_ai.config['num_simulations'] = original_simulations
                    self.mcts_ai.config['thinking_time'] = original_thinking_time
                    return critical_move
        
        print(f"[AI调试] 使用MCTS搜索: 模拟次数={adjusted_simulations}, 思考时间={thinking_time_limit}s, 线程数={self.mcts_ai.config['num_threads']}")
        
        # 如果没有关键威胁，使用MCTS进行深度搜索
        # 转换棋盘格式
        mcts_board = ConstantsMapper.map_board(board)
        
        # 执行MCTS搜索
        best_move = self.mcts_ai.search(mcts_board, mcts_player)
        
        # 转换回原始坐标系统
        result_move = ConstantsMapper.map_back_move(best_move)
        
        # 检查坐标是否有效
        if (0 <= result_move[0] < board_size and 0 <= result_move[1] < board_size and 
            board[result_move[0]][result_move[1]] == 0):
            total_elapsed = (time.time() - start_time) * 1000
            print(f"[AI调试] MCTS搜索完成: 选择位置={result_move}, 总耗时={total_elapsed:.2f}ms")
            
            # 恢复原始配置
            self.mcts_ai.config['num_simulations'] = original_simulations
            self.mcts_ai.config['thinking_time'] = original_thinking_time
            
            return result_move
        
        # 如果MCTS返回无效位置，选择中心点作为后备
        print("[AI调试] MCTS返回无效位置，使用中心点作为后备")
        backup_move = (board_size // 2, board_size // 2)
        
        # 恢复原始配置
        self.mcts_ai.config['num_simulations'] = original_simulations
        self.mcts_ai.config['thinking_time'] = original_thinking_time
        
        return backup_move
    
    def evaluate_position(self, board: List[List[int]], player: int) -> float:
        """
        评估棋盘位置，用于调试和分析
        
        Args:
            board: 当前棋盘状态
            player: 要评估的玩家
            
        Returns:
            评分值
        """
        mcts_player = ConstantsMapper.map_player(player)
        return self.mcts_ai.evaluate_position(board, mcts_player)
    
    def get_move_quality(self, board: List[List[int]], row: int, col: int, player: int) -> float:
        """
        评估特定落子的质量
        
        Args:
            board: 当前棋盘状态
            row: 行号
            col: 列号
            player: 玩家
            
        Returns:
            质量评分
        """
        # 复制棋盘并模拟落子
        board_copy = [row[:] for row in board]
        board_copy[row][col] = player
        
        # 评估落子后的局面
        return self.evaluate_position(board_copy, player)

# 导出适配器类，使其可以在游戏中直接导入
__all__ = ['AIMCTSPlayer']

if __name__ == "__main__":
    # 简单测试
    ai = AIMCTSPlayer(difficulty="medium")
    # 创建空棋盘
    empty_board = [[0 for _ in range(15)] for _ in range(15)]
    
    print("测试AI是否能找到有效落子位置...")
    move = ai.find_best_move(empty_board, 1, max_time=0.5)
    print(f"AI落子位置: {move}")
    
    # 测试特定局面
    board_with_pattern = [[0 for _ in range(15)] for _ in range(15)]
    # 创建四连子
    for i in range(4):
        board_with_pattern[7][6 + i] = 1  # 黑棋四连
    
    print("\n测试AI是否能检测到关键落子...")
    move = ai.find_best_move(board_with_pattern, 1, max_time=1.0)
    print(f"AI在关键局面的落子位置: {move}")
